/*
 * Copyright 2019 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.example.fep.netty.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.example.fep.netty.common.FepProtocolMsg;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description：
 * netty 服务端 处理器
 *
 * @author : chenjie03@imdada.cn
 * Date: 2020/5/26
 * Time: 13:50
 */
@Sharable
public final class ServerMainHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(ServerMainHandler.class);

    /**
     * NettyServer
     */
    private NettyServer server;

    /**
     * 服务端自定义消息处理器
     */
    private ServerHandler handler;

    public ServerMainHandler(NettyServer server, ServerHandler handler) {
        this.server = server;
        this.handler = handler;
    }

    /**
     * 接收客户端消息
     *
     * @param ctx channel上下文
     * @param msg 消息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        FepProtocolMsg message = (FepProtocolMsg) msg;
        logger.info("server recived  => {}", message);
        if (message == null) {
            return;
        }
        try {
            switch (message.getType()) {
                case FepProtocolMsg.Constants.TYPE_HEART_BEAT:
                    //客户端汇报心跳
                    String orgCode = message.getBody();
                    Channel channel = ctx.channel();
                    String channelId = channel.id().asLongText();
                    //缓存channel-orgCode映射
                    server.putClient(orgCode, channelId);
                    server.putChannel(channelId, channel);
                    //心跳接收成功
                    FepProtocolMsg resp = new FepProtocolMsg(FepProtocolMsg.Constants.TYPE_HEART_BEAT, "收到" + orgCode + "的心跳");
                    ctx.writeAndFlush(resp);
                    //服务端-心跳事件
                    handler.activeHandler(message);
                    break;
                case FepProtocolMsg.Constants.TYPE_QUERY:
                    //远程查询
                    handler.queryHandler(message);
                    break;
                case FepProtocolMsg.Constants.TYPE_LOG:
                    //远程日志
                    handler.logHandler(message);
                    break;
                case FepProtocolMsg.Constants.TYPE_ALARM:
                    //远程报警
                    handler.alarmHandler(message);
                    break;
                case FepProtocolMsg.Constants.TYPE_MONITOR:
                    //远程监控
                    handler.monitorHandler(message);
                    break;
                default:
                    //远程监控
                    handler.otherHandler(message);
                    break;
            }
        } catch (Exception e) {
            logger.error("server handler error:", e);
        } finally {
            //msg引用减1，主动释放buf
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 空闲发送失连报警
     *
     * @param ctx channel上下文
     * @param evt 事件
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        //移除无效channel
        String channelId = "";
        try {
            channelId = ctx.channel().id().asLongText();
            server.removeChannel(channelId);
            //失连事件
            handler.lostHandler(channelId);
        } catch (Exception e) {
        }
        logger.info("server : client[{}] 心跳丢失", channelId);
        if (evt instanceof IdleStateEvent && IdleState.READER_IDLE == ((IdleStateEvent) evt).state()) {
            ctx.close();
        }
    }

    /**
     * 异常处理
     *
     * @param ctx   channel上下文
     * @param cause 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("server : 异常=", cause);
        ctx.close();
    }
}
